#include <os/debug.h>
#include <os/driver.h>
#include <os/timer.h>

#include <driver/view/view.h>
#include <driver/view/env.h>
#include <driver/view/region.h>
#include <driver/view/keyboard.h>
#include <driver/view/msg.h>
#include <driver/view/mouse.h>

// activity view
static view_t *view_activity = NULL;
// middle view
static view_t *view_middle = NULL;
// monitor view
static view_t *monitor_view = NULL;

// mouse hover view
static view_t *mouse_hover_view = NULL;
// resize view
static view_t *resize_view = NULL;
// drag view
static view_t *drag_view = NULL;
// shade view
static view_t *shade_view = NULL;
static view_rect_t shade_rect;

// view last position
extern int view_last_x, view_last_y;

// max windows rect,default is whole desktop
view_rect_t windows_maxim_rect;

int ViewEnvInit()
{
    shade_view = ViewCreate(0, 0, view_screen.width, view_screen.height, 0);
    if (!shade_view)
        return -1;

    ViewClear(shade_view);
    // hide view
    ViewSetType(shade_view, VIEW_TYPE_FLOAT);
    ViewSetZ(shade_view, -1);
    ViewRectReset(&shade_rect);
    ViewRectInit(&windows_maxim_rect, 0, 0, view_screen.width, view_screen.height);
    return 0;
}

int ViewEnvExit()
{
    view_activity = NULL;
    view_middle = NULL;

    mouse_hover_view = NULL;
    resize_view = NULL;
    drag_view = NULL;
    if (shade_view)
    {
        ViewHide(shade_view);
        ViewDestroy(shade_view);
        shade_view = NULL;
    }
    return 0;
}

view_t *ViewEnvGetActivity()
{
    return view_activity;
}

void ViewEnvSetActivity(view_t *view)
{
    view_activity = view;
    KPrint("[view env] set view activity to %d\n",view->id);
}

void ViewEnvSetHover(view_t *view)
{
    mouse_hover_view = view;
    KPrint("[view env] set view activity to %d\n",view->id);
}

uint32_t ViewEnvGetScreenSize()
{
    return (view_screen.width << 16) | view_screen.height;
}

uint32_t ViewEnvGetMousePos()
{
    return (view_mouse.x << 16) | view_mouse.y;
}

uint32_t ViewEnvGetLastPos()
{
    return (view_last_x << 16) | view_last_y;
}

view_t *ViewEnvGetMiddle()
{
    return view_middle;
}

void ViewEnvSetMiddle(view_t *view)
{
    view_middle = view;
}

int ViewEnvTryActivity(view_t *view)
{
    view_t *activity = view_activity;
    int val = 0;

    // no activate view
    if (activity != view)
    {
        KPrint("[view env] try activity view %d type %d\n", view->id,view->type);
        view_msg_t m;
        ViewMsgReset(&m);
        if (activity) // exit activity
        {
            ViewMsgHeader(&m, VIEW_MSG_INACTIVATE, activity->id);
            val = ViewTryPutMsg(activity, &m);
            ViewEnvSendToMonitor(activity, VIEW_MSG_INACTIVATE, 0);
        }
        ViewEnvSetActivity(view); // set to activate
        if (view)
        {
            // set to top layout
            if (view->type == VIEW_TYPE_WINDOWS)
            {
               ViewMoveUnderView(view, view_middle);      
            }
            ViewMsgHeader(&m, VIEW_MSG_ACTIVATE, view->id);
            val = ViewTryPutMsg(view, &m);
            ViewEnvSendToMonitor(view, VIEW_MSG_ACTIVATE, 0);   
             KPrint("[env] try activity view %d return %d\n",view->id,val);
        }

        //ViewShowDump();
    }
    return val;
}

void ViewEnvDoMouseHover(view_t *view, view_msg_t *msg, int local_mx, int local_my)
{
    // no mouse hover view
    if (mouse_hover_view != view)
    {
        view_msg_t m;
        ViewMsgReset(&m);
        if (view)
        {
            ViewMsgHeader(&m, VIEW_MSG_ENTER, view->id);
            ViewMsgData(&m, local_mx, local_my, msg->data0, msg->data1);
            ViewTryPutMsg(view, &m);
        }

        if (mouse_hover_view) // leave hover view
        {
            ViewMsgHeader(&m, VIEW_MSG_LEAVE, mouse_hover_view->id);
            ViewMsgData(&m, msg->data0 - mouse_hover_view->x, msg->data1 - mouse_hover_view->y, 0, 0);
            ViewTryPutMsg(mouse_hover_view, &m);
        }

        if (view_mouse.status != VIEW_MOUSE_NORMAL)
        {
            ViewMouseSetStatus(VIEW_MOUSE_NORMAL);
            view_mouse.click_x = -1;
            view_mouse.click_y = -1;
        }
        mouse_hover_view = view; // set to mouse hover view 
        KPrint("[env] mouse hover view change to %d\n",mouse_hover_view->id);
    }
}

int ViewEnvTryResize(view_t *view, view_rect_t *rect)
{
    if (!view || !rect)
        return -1;
    KPrint("[env] view %d resize to %d %d %d %d\n",view->id,rect->x,rect->y,rect->w.uw,rect->h.uh);
    if (ViewResize(view, rect->x, rect->y, rect->w.uw, rect->h.uh) < 0)
        return -1;
    view_msg_t m;
    ViewMsgHeader(&m, VIEW_MSG_RESIZE, view->id);
    ViewMsgData(&m, rect->x, rect->y, rect->w.uw, rect->h.uh);
    return ViewTryPutMsg(view, &m);
}

int ViewEnvTryResizeEx(view_t *view, int width, int height)
{
    view_rect_t rect;
    rect.x = view->x;
    rect.y = view->y;
    rect.w.uw = width;
    rect.h.uh = height;
    return ViewEnvTryResize(view, &rect);
}

static void ViewEnvTimerCallback(timer_t *timer, void *arg)
{
    view_t *view = (view_t *)arg;
    if (!view)
        return;
    view_msg_t msg;
    ViewMsgHeader(&msg, VIEW_MSG_TIMER, view->id);
    ViewMsgData(&msg, timer->id, 0, 0, 0);
    ViewPutMsg(view, &msg, VIEW_MSG_NOWAIT);
    KMemAlloc(sizeof(timer_t));
}

int ViewEnvAddTimer(view_t *view, uint32_t interval)
{
    timer_t *timer = TimerAlloc();
    if (!timer)
        return -1;
    TimerInit(timer, MSEC_TO_TICKS(interval), view, ViewEnvTimerCallback);
    TimerAdd(timer);
    return timer->id;
}

int ViewEnvRestartTimer(view_t *view, uint32_t timer_id, uint32_t interval)
{
    timer_t *timer = TimerFind(timer_id);
    // timer present just modify and return
    if (timer)
    {
        TimerModify(timer, MSEC_TO_TICKS(interval));
        return 0;
    }
    // restart init new timer
    timer = TimerAlloc(); // alloc timer
    if (!timer)
        return -1;
    TimerInit(timer, MSEC_TO_TICKS(interval), view, ViewEnvTimerCallback);
    timer->id = timer_id;
    TimerAdd(timer);
    return 0;
}

int ViewEnvDelTimer(view_t *view, uint32_t timer_id)
{
    timer_t *timer = TimerFind(timer_id);
    if (!timer)
        return -1;
    if (TimerCancel(timer) < 0)
        return 0;
    KMemFree(timer);
}

int ViewEnvSendToMonitor(view_t *view, int mid, int arg)
{
    if (!monitor_view || !view)
    {
        KPrint("[view] no monitor\n");
        return -1;
    }
    // no monitor self
    if (monitor_view->id == view->id)
    {
        return -1;
    }
    // send to monitor view
    view_msg_t msg;
    ViewMsgHeader(&msg, mid, view->id);
    ViewMsgData(&msg, view->type, arg, 0, 0);
    return ViewPutMsg(monitor_view, &msg, VIEW_MSG_NOWAIT);
}

int ViewEnvSetMonitor(view_t *view, int is_montor)
{
    if (!view)
        return -1;
    if (is_montor)
        monitor_view = view;
    else
    {
        if (monitor_view == view)
            monitor_view = NULL;
    }
    KPrint("monitor view %d\n", view->id);
    return 0;
}

int ViewEnvCheckMonitorExit(view_t *view)
{
    if (!view)
        return -1;
    if (view == monitor_view)
        monitor_view = NULL;
    return 0;
}

int ViewEnvSetWinmaximRect(view_rect_t *rect)
{
    if (!rect)
        return -1;
    windows_maxim_rect = *rect;
    KPrint("[view] set max rect to %d %d %d %d\n", windows_maxim_rect.x, windows_maxim_rect.y, windows_maxim_rect.w.uw, windows_maxim_rect.h.uh);
    return 0;
}

int ViewEnvGetWinmaximRect(view_rect_t *rect)
{
    if (!rect)
        return -1;
    *rect = windows_maxim_rect;
    KPrint("[view] get max rect to %d %d %d %d\n", windows_maxim_rect.x, windows_maxim_rect.y, windows_maxim_rect.w.uw, windows_maxim_rect.h.uh);

    return 0;
}

int ViewEnvDoDrag(view_t *view, view_msg_t *msg, int local_mx, int local_my)
{
    if (view->type == VIEW_TYPE_FIXED || !(view->attr & VIEW_ATTR_MOVEABLE))
    {
        return 0;
    }
    if (!ViewDragRectCheck(view, local_mx, local_my))
    {
        return 0;
    }
    if (ViewMsgGetId(msg) == VIEW_MSG_MOUSE_LBTN_DOWN)
    {
        KPrint("[view] env set drag view to %d pos %d %d\n",view->id,local_mx,local_my);
        view_mouse.local_x = local_mx;
        view_mouse.local_y = local_my;
        drag_view = view;
    }
}

view_t *ViewEnvFindHoverView()
{
    list_t *view_show_list_head = ViewGetShowList();
    view_t *view;

    list_traversal_all_owner_to_next(view, view_show_list_head, list)
    {
        if (view == view_mouse.view)
        {
            continue;
        }
        int local_mx, local_my;
        local_mx = view_mouse.x - view->x;
        local_my = view_mouse.y - view->y;
        if (local_mx > 0 && local_mx < view->width && local_my > 0 && local_my < view->height)
        {
            KPrint("[view mouse] find mouse hover view %d\n", view->id);
            return view;
        }
    }
    return NULL;
}

int ViewCalcResize(view_t *view, int mx, int my, view_rect_t *out_rect)
{
    if (!view)
        return -1;

    int sx, sy; // screen x,y
    sx = mx - view_mouse.click_x;
    sy = my - view_mouse.click_y;
    if (!sx || !sy)
        return -1;

    int cx, cy; // center x,y
    cx = view->width / 2 + view->x;
    cy = view->height / 2 + view->y;

    view_rect_t rect;
    ViewRectReset(&rect);

    switch (view_mouse.status)
    {
    case VIEW_MOUSE_VRESIZE:
        if (view_mouse.click_y < cy)
        {
            rect.x = view->x;
            rect.y = view->y + sy;
            rect.w.sw = view->width;
            rect.h.sh = view->height + -sy;
        }
        else
        {
            rect.x = view->x;
            rect.y = view->y;
            rect.w.sw = view->width;
            rect.h.sh - view->height + sy;
        }
        break;
    case VIEW_MOUSE_HRESIZE:
        if (view_mouse.click_x < cx)
        {
            rect.x = view->x + sx;
            rect.y = view->y;
            rect.w.sw = view->width + -sx;
            rect.h.sh = view->height;
        }
        else
        {
            rect.x = view->x;
            rect.y = view->y;
            rect.w.sw = view->width + sx;
            rect.h.sh = view->height;
        }
        break;
    case VIEW_MOUSE_DRESIZE1:
        if (view_mouse.click_x < cx)
        {
            rect.x = view->x + sx;
            rect.y = view->y;
            rect.w.sw = view->width + -sx;
            rect.h.sh = view->height + sy;
        }
        else
        {
            rect.x = view->x;
            rect.y = view->y;
            rect.w.sw = view->width + sx;
            rect.h.sh = view->height + -sy;
        }
        break;
    case VIEW_MOUSE_DRESIZE2:
        if (view_mouse.click_x < cx)
        {
            rect.x = view->x + sx;
            rect.y = view->y + sy;
            rect.w.sw = view->width + -sx;
            rect.h.sh = view->height + -sy;
        }
        else
        {
            rect.x = view->x;
            rect.y = view->y;
            rect.w.sw = view->width + sx;
            rect.h.sh = view->height + sy;
        }
        break;
    default:
        break;
    }
    // check rect
    if (rect.w.sw <= 0 || rect.h.sh <= 0)
        return -1;
    if (rect.w.sw < view->width_min)
        rect.w.sw = view->width_min;
    if (rect.h.sh < view->height_min)
        rect.h.sh = view->height_min;

    *out_rect = rect;
    return 0;
}

// filter mouse message
int ViewEnvFilterMouseMsg(view_msg_t *msg)
{
    // mouse left button down
    if (ViewMsgGetId(msg) == VIEW_MSG_MOUSE_LBTN_DOWN)
    {
        if (resize_view) // had resize view
            return 0;
        if (drag_view) // had drag view
            return 0;
    }
    // left key up
    if (ViewMsgGetId(msg) == VIEW_MSG_MOUSE_LBTN_UP)
    {
        view_rect_t rect;
        if (resize_view)
        {
            // send resize msg
            if (!ViewCalcResize(resize_view, msg->data0, msg->data1, &rect))
            {
                ViewEnvTryResize(resize_view, &rect);
            }
            resize_view = NULL;
            // set mouse status
            ViewMouseSetStatus(VIEW_MOUSE_NORMAL);
            view_mouse.click_x = -1;
            view_mouse.click_y = -1;
            return 0;
        }
        if (drag_view)
        {
            if (ViewMouseIsStatus(VIEW_MOUSE_HOLD))
            {
                int wx = view_mouse.x - view_mouse.local_x;
                int wy = view_mouse.y - view_mouse.local_y;
                ViewSetXY(drag_view, wx, wy);
                view_msg_t m;
                ViewMsgReset(&m);
                ViewMsgHeader(&m, VIEW_MSG_MOVE, drag_view->id);
                ViewMsgData(&m, drag_view->x, drag_view->y, 0, 0);
                ViewTryPutMsg(drag_view, &m);
            }
            drag_view = NULL;
            ViewMouseSetStatus(VIEW_MOUSE_NORMAL);
            return -1;
        }
    }
    if (msg->id == VIEW_MSG_MOUSE_MOTION)
    {
        if (resize_view)
        {
            view_rect_t rect;
            if (!ViewCalcResize(resize_view, msg->data0, msg->data1, &rect))
            {
                ViewEnvTryResize(resize_view, &rect);
                // updata click pos
                view_mouse.click_x = msg->data0;
                view_mouse.click_y = msg->data1;
            }
            return 0;
        }
        if (drag_view)
        {
            int wx = view_mouse.x - view_mouse.local_x;
            int wy = view_mouse.y - view_mouse.local_y;

            ViewSetXY(drag_view, wx, wy);
            ViewMouseSetStatus(VIEW_MOUSE_HOLD);
            return 0;
        }
    }
    return -1;
}

int ViewEnvDoResize(view_t *view, view_msg_t *msg, int local_mx, int local_my)
{
    if (view->type == VIEW_TYPE_FIXED || !(view->attr & VIEW_ATTR_RESIZEABLE))
        return -1;

    //KPrint("[env] do_resize: mouse x %d y %d resize area l %d r %d t %d b %d\n",local_mx,local_my,view->resize_regions.left,view->resize_regions.right,view->resize_regions.top,view->resize_regions.bottom);
    if (ViewRegionValid(&view->resize_regions))
    {
        if (!ViewRegionInRange(&view->resize_regions, local_mx, local_my)) // no in region
        {
            if (ViewMsgGetId(msg) == VIEW_MSG_MOUSE_LBTN_DOWN)
            {
                resize_view = view;
                view_mouse.click_x = msg->data0;
                view_mouse.click_y = msg->data1;
            }

            // according view position to set status
            if (local_mx < view->resize_regions.left)
            {
                if (local_my < view->resize_regions.top)
                    ViewMouseSetStatus(VIEW_MOUSE_DRESIZE2);
                else if (local_mx > view->resize_regions.bottom)
                    ViewMouseSetStatus(VIEW_MOUSE_DRESIZE1);
                else
                    ViewMouseSetStatus(VIEW_MOUSE_HRESIZE);
            }
            else
            {
                if (local_mx >= view->resize_regions.right)
                {
                    if (local_my < view->resize_regions.top)
                        ViewMouseSetStatus(VIEW_MOUSE_DRESIZE1);
                    else if (local_my >= view->resize_regions.bottom)
                        ViewMouseSetStatus(VIEW_MOUSE_DRESIZE2);
                    else
                        ViewMouseSetStatus(VIEW_MOUSE_HRESIZE);
                }
                else
                {
                    if (local_my < view->resize_regions.top)
                        ViewMouseSetStatus(VIEW_MOUSE_VRESIZE);
                    else if (local_my >= view->resize_regions.bottom)
                    {
                        ViewMouseSetStatus(VIEW_MOUSE_VRESIZE);
                    }
                }
            }
            return 0;
        }
        else
        {
            if ((view_mouse.status == VIEW_MOUSE_HRESIZE) || (view_mouse.status == VIEW_MOUSE_VRESIZE) || (view_mouse.status == VIEW_MOUSE_DRESIZE1) || (view_mouse.status == VIEW_MOUSE_DRESIZE2) || (view_mouse.status == VIEW_MOUSE_RESIZEALL))
            {
                if (view_mouse.click_x != -1 && view_mouse.click_y != -1)
                {
                    view_mouse.click_x = -1;
                    view_mouse.click_y = -1;
                }
            }
        }
    }
    return -1;
}

// reset hover and activity view
int ViewEnvResetHoverAndActivity()
{
    // changed activity view
    ViewEnvTryActivity(ViewFindByZ(ViewEnvGetMiddle()->z - 1));
    // changed mouse hover view
    ViewEnvSetHover(NULL);
    // find mouse hover view
    view_t *view = ViewEnvFindHoverView();
    if (!view)
        return -1;
    view_msg_t msg;
    ViewMsgData(&msg, view_mouse.x, view_mouse.y, 0, 0);
    ViewEnvDoMouseHover(view, &msg, view_mouse.x - view->x, view_mouse.y - view->y);
    return 0;
}